#include "Arduino.h"
#include "lcd.h"
#include "stdlib.h"

//管理LCD重要参数
//默认为竖屏
_lcd_dev lcddev;

//画笔颜色,背景颜色
uint16_t POINT_COLOR = 0x0000,BACK_COLOR = 0xFFFF;   

//写操作
#ifdef DIRECT_OPERATION_REGISTER

	#define dir_mask ((1 << LCD_D0) | (1 << LCD_D1) | (1 << LCD_D2) | (1 << LCD_D3) | (1 << LCD_D4) | (1 << LCD_D5) | (1 << LCD_D6) | (1 << LCD_D7))

	// #define set_mask(C) (((C)&0x80)>>7)<<LCD_D7 | (((C)&0x40)>>6)<<LCD_D6 | (((C)&0x20)>>5)<<LCD_D5 | (((C)&0x10)>>4)<<LCD_D4 | \
	// 					(((C)&0x08)>>3)<<LCD_D3 | (((C)&0x04)>>2)<<LCD_D2 | (((C)&0x02)>>1)<<LCD_D1 | (((C)&0x01)>>0)<<LCD_D0
uint32_t xset_mask[256];
//定义数组初始化宏，在初始化函数中执行PARALLEL_INIT_DATA_BUS;进行初始化
#define PARALLEL_INIT_DATA_BUS               \
  for (int32_t c = 0; c<256; c++)              \
  {                                            \
    xset_mask[c] = 0;                          \
    if ( c & 0x01 ) xset_mask[c] |= (1 << LCD_D0); \
    if ( c & 0x02 ) xset_mask[c] |= (1 << LCD_D1); \
    if ( c & 0x04 ) xset_mask[c] |= (1 << LCD_D2); \
    if ( c & 0x08 ) xset_mask[c] |= (1 << LCD_D3); \
    if ( c & 0x10 ) xset_mask[c] |= (1 << LCD_D4); \
    if ( c & 0x20 ) xset_mask[c] |= (1 << LCD_D5); \
    if ( c & 0x40 ) xset_mask[c] |= (1 << LCD_D6); \
    if ( c & 0x80 ) xset_mask[c] |= (1 << LCD_D7); \
  }                                            \
//查映射表
#define set_mask(C) xset_mask[C];
	//直接操作寄存器，按照具体GPIO配置
	#define WRITE(Data) GPIO.out_w1tc = dir_mask; GPIO.out_w1ts = set_mask((uint8_t)(Data)); 
#else
	#define WRITE(Data){ \
		digitalWrite(LCD_D0,Data&0x01);\
		digitalWrite(LCD_D1,Data&0x02);\
		digitalWrite(LCD_D2,Data&0x04);\
		digitalWrite(LCD_D3,Data&0x08);\
		digitalWrite(LCD_D4,Data&0x10);\
		digitalWrite(LCD_D5,Data&0x20);\
		digitalWrite(LCD_D6,Data&0x40);\
		digitalWrite(LCD_D7,Data&0x80);\
	}
#endif

/*
 初始化指令，每行为一个指令操作，第一位为指令，第二位为参数个数，后边为具体参数
*/
const uint8_t initCmd[] = {
// 指令  参数个数  参数
	0xb9, 3, 0xff,0x83,0x53, //SETEXTC
	0xc6, 1, 0x31, //UADJ
	0x36, 1, 0x80, //TM0x88
	0xb2, 1, 0x00, //SET DISPLAY
	0xb6, 3, 0x80,0x6f,0x6f, //0x62 //VCOM
	//Gamma setting for TIAN MA Panel
	0xe0, 19,0x47,0x77,0x40,0x00,0xc8,0x01,0x03,0x0f,0x00,0x0a,0x73,0x00,0x03,0x01,0xc1,0x08,0x00,0x00,0x0f,
	0x11, 0, // SLPOUT
	0xb3, 1, 0x00,         //LUT
	0x13, 0, //NORMAL  ON
	0x3a, 1, 0X05,
	0x36, 1, 0xd0, 
	0x29, 0 // display on
};

/*****************************************************************************
 * @name       :void LCD_WR_REG(uint8_t data)
 * @date       :2018-08-09 
 * @function   :Write an 8-bit command to the LCD screen
 * @parameters :data:Command value to be written
 * @retvalue   :None
******************************************************************************/
void LCD_WR_REG(uint8_t data)
{ 
	LCD_RS_CLR;
	WRITE(data);
	LCD_WR_CLR;
	LCD_WR_SET;
}

/*****************************************************************************
 * @name       :void LCD_WR_DATA(uint8_t data)
 * @date       :2018-08-09 
 * @function   :Write an 8-bit data to the LCD screen
 * @parameters :data:data value to be written
 * @retvalue   :None
******************************************************************************/
void LCD_WR_DATA(uint8_t data)
{
	LCD_RS_SET;
	WRITE(data);
	LCD_WR_CLR;
	LCD_WR_SET;
}

/*****************************************************************************
 * @name       :void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
 * @date       :2018-08-09 
 * @function   :Write data into registers
 * @parameters :LCD_Reg:Register address
                LCD_RegValue:Data to be written
 * @retvalue   :None
******************************************************************************/
void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
{	
	LCD_WR_REG(LCD_Reg);  
	LCD_WR_DATA(LCD_RegValue);	    		 
}	   

/*****************************************************************************
 * @name       :void LCD_WriteRAM_Prepare(void)
 * @date       :2018-08-09 
 * @function   :Write GRAM
 * @parameters :None
 * @retvalue   :None
******************************************************************************/	 
void LCD_WriteRAM_Prepare(void)
{
	LCD_WR_REG(lcddev.wramcmd);
}	 

/*****************************************************************************
 * @name       :void Lcd_WriteData_16Bit(uint16_t Data)
 * @date       :2018-08-09 
 * @function   :Write an 16-bit command to the LCD screen
 * @parameters :Data:Data to be written
 * @retvalue   :None
******************************************************************************/	 
void Lcd_WriteData_16Bit(uint16_t Data)
{	
	LCD_WR_DATA(Data>>8);
    LCD_WR_DATA(Data);
}

/*****************************************************************************
 * @name       :void LCD_DrawPoint(uint16_t x,uint16_t y)
 * @date       :2018-08-09 
 * @function   :Write a pixel data at a specified location
 * @parameters :x:the x coordinate of the pixel
                y:the y coordinate of the pixel
 * @retvalue   :None
******************************************************************************/	
void LCD_DrawPoint(uint16_t x,uint16_t y)
{
	LCD_SetCursor(x,y);//设置光标位置 
	Lcd_WriteData_16Bit(POINT_COLOR); 
}

uint16_t  LCD_ReadPoint(uint16_t x,uint16_t y){

    return 0;
}

/*****************************************************************************
 * @name       :void LCD_Clear(uint16_t Color)
 * @date       :2018-08-09 
 * @function   :Full screen filled LCD screen
 * @parameters :color:Filled color
 * @retvalue   :None
******************************************************************************/	
void LCD_Clear(uint16_t Color)
{
	unsigned int i,m;  
	LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);   
	for(i=0;i<lcddev.height;i++)
	{
		for(m=0;m<lcddev.width;m++)
		{	
			Lcd_WriteData_16Bit(Color);
		}
	}
} 



/*****************************************************************************
 * @name       :void LCD_RESET(void)
 * @date       :2018-08-09 
 * @function   :Reset LCD screen
 * @parameters :None
 * @retvalue   :None
******************************************************************************/	
void LCD_RESET(void)
{
	LCD_RST_CLR;
	delay(5); 
	LCD_RST_SET;
}


/*****************************************************************************
 * @name       :void LCD_RESET(void)
 * @date       :2018-08-09 
 * @function   :Initialization LCD screen
 * @parameters :None
 * @retvalue   :None
******************************************************************************/	 	 
void LCD_Init(void)
{  		
	PARALLEL_INIT_DATA_BUS;
	
	//GPIO初始化	
	pinMode(LCD_CS,OUTPUT);
	pinMode(LCD_RS,OUTPUT);
	pinMode(LCD_WR,OUTPUT);
	pinMode(LCD_RD,OUTPUT);
	pinMode(LCD_RST,OUTPUT);
	pinMode(LCD_BL,OUTPUT);
	pinMode(LCD_D0,OUTPUT);
	pinMode(LCD_D1,OUTPUT);
	pinMode(LCD_D2,OUTPUT);
	pinMode(LCD_D3,OUTPUT);
	pinMode(LCD_D4,OUTPUT);
	pinMode(LCD_D5,OUTPUT);
	pinMode(LCD_D6,OUTPUT);
	pinMode(LCD_D7,OUTPUT);

   /* hard reset */
	LCD_CS_CLR;
	LCD_RD_SET;
	LCD_RESET();
    delay(5);

	//执行初始化命令	
	uint8_t data, i, numArgs;
	uint8_t addr = 0;
	uint8_t len = sizeof(initCmd);
	while (addr<len) {
		data = initCmd[addr++];
		//写指令
		LCD_WR_REG(data);
		//参数个数
		numArgs=initCmd[addr++];
		i=0;
		while(i++<numArgs){
			data = initCmd[addr++];
			LCD_WR_DATA(data);	 
		}
	}
	
	LCD_direction(USE_HORIZONTAL);//设置LCD显示方向 
    LCD_Clear(WHITE);

	LCD_BL_SET;
}
 
/*****************************************************************************
 * @name       :void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd)
 * @date       :2018-08-09 
 * @function   :Setting LCD display window
 * @parameters :xStar:the bebinning x coordinate of the LCD display window
								yStar:the bebinning y coordinate of the LCD display window
								xEnd:the endning x coordinate of the LCD display window
								yEnd:the endning y coordinate of the LCD display window
 * @retvalue   :None
******************************************************************************/ 
void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd)
{	
	LCD_WR_REG(lcddev.setxcmd);	
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(xStar+lcddev.xoffset);		
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(xEnd+lcddev.xoffset);

	LCD_WR_REG(lcddev.setycmd);	
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(yStar+lcddev.yoffset);		
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(yEnd+lcddev.yoffset);

	LCD_WriteRAM_Prepare();	//开始写入GRAM			
}   

/*****************************************************************************
 * @name       :void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
 * @date       :2018-08-09 
 * @function   :Set coordinate value
 * @parameters :Xpos:the  x coordinate of the pixel
								Ypos:the  y coordinate of the pixel
 * @retvalue   :None
******************************************************************************/ 
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
{	  	    			
	LCD_SetWindows(Xpos,Ypos,Xpos,Ypos);	
} 

/*****************************************************************************
 * @name       :void LCD_direction(uint8_t direction)
 * @date       :2018-08-09 
 * @function   :Setting the display direction of LCD screen
 * @parameters :direction:
 *  0-0 degree
 *	1-90 degree
 *	2-180 degree
 * 	3-270 degree
 * @retvalue   :None
******************************************************************************/ 
void LCD_direction(uint8_t direction)
{ 
	lcddev.setxcmd=0x2A;
	lcddev.setycmd=0x2B;
	lcddev.wramcmd=0x2C;
	lcddev.dir=direction%4;		
	//RGB顺序 0x80:RGB 0x00:GBR
	uint8_t colorMode= BGR== 1 ? 0x08 : 0x00;
	switch(lcddev.dir){		  
		case 0:						 	 		
			lcddev.width=LCD_W;
			lcddev.height=LCD_H;	
			lcddev.xoffset=0;
			lcddev.yoffset=0;
			LCD_WriteReg(0x36,colorMode|(1<<6)|(1<<7));//MY==1,MX==1,MV==0
		break;
		case 1:
			lcddev.width=LCD_H;
			lcddev.height=LCD_W;
			lcddev.xoffset=0;
			lcddev.yoffset=0;
			LCD_WriteReg(0x36,colorMode|(1<<5)|(1<<7));//MY==1,MX==0,MV==1
		break;
		case 2:						 	 		
			lcddev.width=LCD_W;
			lcddev.height=LCD_H;
      		lcddev.xoffset=0;
			lcddev.yoffset=0;			
			LCD_WriteReg(0x36,colorMode);//MY==0,MX==0,MV==0
		break;
		case 3:
			lcddev.width=LCD_H;
			lcddev.height=LCD_W;
			lcddev.xoffset=0;
			lcddev.yoffset=0;
			LCD_WriteReg(0x36,colorMode|(1<<6)|(1<<5));//MY==0,MX==1,MV==1
		break;	
		default:break;
	}		
}	 
 
//  /*****************************************************************************
//  * @name       :void LCD_ShowChar(uint16_t x,uint16_t y,uint16_t fc, uint16_t bc, uint8_t num,uint8_t size,uint8_t mode)
//  * @date       :2018-08-09 
//  * @function   :Display a single English character
//  * @parameters :x:the bebinning x coordinate of the Character display position
//                 y:the bebinning y coordinate of the Character display position
// 								fc:the color value of display character
// 								bc:the background color of display character
// 								num:the ascii code of display character(0~94)
// 								size:the size of display character
// 								mode:0-no overlying,1-overlying
//  * @retvalue   :None
// ******************************************************************************/ 
// void LCD_ShowChar(uint16_t x,uint16_t y,uint16_t fc, uint16_t bc, uint8_t num,uint8_t size,uint8_t mode)
// {  
//     uint8_t temp;
//     uint8_t pos,t;
// 	uint16_t colortemp=POINT_COLOR;      
		   
// 	num=num-' ';//得到偏移后的值
// 	LCD_SetWindows(x,y,x+size/2-1,y+size-1);//设置单个文字显示窗口
// 	if(!mode) //非叠加方式
// 	{		
// 		for(pos=0;pos<size;pos++)
// 		{
// 			if(size==12)temp=asc2_1206[num][pos];//调用1206字体
// 			else temp=asc2_1608[num][pos];		 //调用1608字体
// 			for(t=0;t<size/2;t++)
// 		    {                 
// 		        if(temp&0x01)Lcd_WriteData_16Bit(fc); 
// 				else Lcd_WriteData_16Bit(bc); 
// 				temp>>=1; 
				
// 		    }
			
// 		}	
// 	}else//叠加方式
// 	{
// 		for(pos=0;pos<size;pos++)
// 		{
// 			if(size==12)temp=asc2_1206[num][pos];//调用1206字体
// 			else temp=asc2_1608[num][pos];		 //调用1608字体
// 			for(t=0;t<size/2;t++)
// 		    {   
// 				POINT_COLOR=fc;              
// 		        if(temp&0x01)LCD_DrawPoint(x+t,y+pos);//画一个点    
// 		        temp>>=1; 
// 		    }
// 		}
// 	}
// 	POINT_COLOR=colortemp;	
// 	LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复窗口为全屏    	   	 	  
// }

// /*****************************************************************************
//  * @name       :void LCD_ShowString(uint16_t x,uint16_t y,uint8_t size,uint8_t *p,uint8_t mode)
//  * @date       :2018-08-09 
//  * @function   :Display English string
//  * @parameters :x:the bebinning x coordinate of the English string
//                 y:the bebinning y coordinate of the English string
// 								p:the start address of the English string
// 								size:the size of display character
// 								mode:0-no overlying,1-overlying
//  * @retvalue   :None
// ******************************************************************************/   	  
// void LCD_ShowString(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc,const uint8_t *str,uint8_t size,uint8_t mode)
// {         
//    uint16_t x0=x;							  	  
//     while(*str!=0)//数据未结束
//     { 
//         if(*str==0x0D)//换行符号
//         {         
//             y+=size;
//             x=x0;
//             str++; 
//         }  
//         else
//         {
//             if(size>16)//字库中没有集成12X24 16X32的英文字体,用8X16代替
//             {  
//             LCD_ShowChar(x,y,fc,bc,*str,16,mode);
//             x+=8; //字符,为全字的一半 
//             }
//             else
//             {
//             LCD_ShowChar(x,y,fc,bc,*str,size,mode);
//             x+=size/2; //字符,为全字的一半 
//             }
//         } 
//         str++; 
        
//     }			 
// } 